#ifndef HTTP_HTTP_CLIENT_H_
#define HTTP_HTTP_CLIENT_H_

#include <string>
#include <memory>
#include <functional>
#include <fermat/net/tcp_client.h>
#include <fermat/net/tcp_connection.h>
#include <fermat/event/event_loop.h>
#include <fermat/http/http_request_builder.h>
#include <fermat/http/http_response.h>
#include <fermat/common/any.h>

namespace fermat {
namespace http {

class HttpClient;
typedef std::function<ResponsePtr()> ResponseFactory;
typedef std::function<bool(ResponsePtr&, HttpClient*)>         ResponseReady;
typedef std::function<bool(RequestBuilderPtr&, std::string&)>  RequestWrite;
typedef std::function<bool(HttpClient*)>                       ClientCB;

class HttpClient {
public:
	HttpClient(fermat::event::EventLoopPtr loop, fermat::net::SocketAddress &_serverAddr, std::string &name);
	~HttpClient();
	
	void connect();
	void shutdown();
	bool postRequest(RequestBuilderPtr &req);
	
	void setResponseReady(const ResponseReady &cb);
	void setRequestWrite(const RequestWrite& cb);
	void setResponseFactory(const ResponseFactory &cb);
	void setConnectCB(const ClientCB &cb) { _onConnect = cb;}
	void setCloseCB(const ClientCB &cb) { _onClose = cb; }
	void setWriteComplete(const ClientCB &cb) { _writeComplete = cb; }
	fermat::net::TcpConnectionPtr getConnection();
	void setContext(const fermat::any &any);
	const fermat::any getContext();
	fermat::any *getMutableContext();

	fermat::Timestamp lastRecvTime() { return _lastrecv; }
private:
	void onMessage(const fermat::net::TcpConnectionPtr &conn,
                              PageBufferSliceQueuePtr buf,
                              fermat::Timestamp recvtime);
	void onConnection(const fermat::net::TcpConnectionPtr &conn);
	void onWriteComplete(const fermat::net::TcpConnectionPtr &conn);

	
private:
	void sendReques();
	void sendRequestInloop();
	bool isSendComplete();
private:
	ResponseFactory                              _responseFactory;
	ResponseReady                                _resReady;
	RequestWrite                                 _reqWrite;
	ClientCB                                     _resParseError;
	ClientCB                                     _onConnect;
	ClientCB                                     _onClose;
	ClientCB                                     _writeComplete;
private:
	fermat::Timestamp                            _lastrecv;
	fermat::any                                  _context;
	fermat::net::SocketAddress                   _serverAddr;
	fermat::event::EventLoopPtr                  _loop;
	fermat::net::TcpClient                       _client;
	std::weak_ptr<fermat::net::TcpConnection>    _conn;
	RequestBuilderPtr                            _currentRequest;
	ResponsePtr                                  _currentResponse;
	
};
}
}
#endif